package org.aurora.framework.boot.common.aop;

import org.springframework.aop.support.AopUtils;
import org.springframework.aop.support.StaticMethodMatcherPointcutAdvisor;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.lang.NonNull;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Objects;

/**
 * AbstractAnnotationPointcutAdvisor
 * <pre>
 * description: 注解切面通知
 * create date: 2025-03-21 20:57:04
 * </pre>
 *
 * @author cao.yong
 */
public abstract class AbstractAnnotationPointcutAdvisor extends StaticMethodMatcherPointcutAdvisor {
    @Override
    public boolean matches(@NonNull Method method, @NonNull Class<?> target) {
        if (isAnnotationPresent(method, target)) {
            return true;
        }
        // The method may be on an interface, so let's check on the target class as well.
        Method mostSpecificMethod = AopUtils.getMostSpecificMethod(method, target);
        return (mostSpecificMethod != method && isAnnotationPresent(mostSpecificMethod, null));
    }

    public abstract Class<? extends Annotation> getAnnotationClass();

    private boolean isAnnotationPresent(Method method, Class<?> target) {
        boolean having = true;
        Annotation an = AnnotationUtils.findAnnotation(method, this.getAnnotationClass());
        if (Objects.isNull(an)) {
            if (Objects.nonNull(target)) {
                // Proxy classes never have annotations on their redeclared methods.
                if (Proxy.isProxyClass(target)) {
                    having = false;
                } else {
                    an = AnnotationUtils.findAnnotation(target, this.getAnnotationClass());
                    if (Objects.isNull(an)) {
                        having = false;
                    }
                }
            } else {
                having = false;
            }
        }
        return having;
    }

}
